package io.kiki.sba.registry.server.clientInterface.providedata;

import io.kiki.sba.registry.common.model.constants.ValueConstants;
import io.kiki.sba.registry.common.model.metaserver.CompressPushSwitch;
import io.kiki.sba.registry.common.model.metaserver.ProvideData;
import io.kiki.sba.registry.compress.CompressUtils;
import io.kiki.sba.registry.compress.Compressor;
import io.kiki.sba.registry.core.model.Data;
import io.kiki.sba.registry.net.NetUtil;
import io.kiki.sba.registry.server.clientInterface.bootstrap.Config;
import io.kiki.sba.registry.server.shared.providedata.AbstractFetchSystemPropertyService;
import io.kiki.sba.registry.server.shared.providedata.SystemDataStorage;
import io.kiki.sba.registry.util.JsonUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;

public class CompressPushService extends AbstractFetchSystemPropertyService<CompressPushService.CompressStorage> {
    private static final Logger logger = LoggerFactory.getLogger(CompressPushSwitch.class);

    @Autowired
    private Config config;

    public CompressPushService() {
        super(ValueConstants.COMPRESS_PUSH_SWITCH_DATA_ID, new CompressStorage(INIT_VERSION, CompressPushSwitch.defaultSwitch()));
    }

    private static boolean compressEnabled(CompressPushSwitch compressPushSwitch, String clientIp) {
        if (compressPushSwitch.isEnabled()) {
            return true;
        }
        if (compressPushSwitch.getEnabledSessions().contains(NetUtil.getLocalAddress().getHostAddress())) {
            return true;
        }
        if (compressPushSwitch.getEnabledClients().contains(clientIp)) {
            return true;
        }
        return false;
    }

    private static int dataBoxesMapSize(Map<String, List<Data>> dataBoxesMap) {
        int size = 0;
        for (Map.Entry<String, List<Data>> boxesEntry : dataBoxesMap.entrySet()) {
            size += boxesEntry.getKey().length();
            for (Data box : boxesEntry.getValue()) {
                if (!StringUtils.isBlank(box.getData())) {
                    size += box.getData().length();
                }
            }
        }
        return size;
    }

    @Override
    protected int getSystemPropertyIntervalMillis() {
        return config.getSystemPropertyIntervalMillis();
    }

    @Override
    protected boolean doProcess(CompressStorage expect, ProvideData data) {
        final String switchString = ProvideData.toString(data);
        if (StringUtils.isBlank(switchString)) {
            logger.info("Fetch session push compressed enabled content empty");
            return true;
        }
        try {
            CompressPushSwitch compressPushSwitch = JsonUtils.read(switchString, CompressPushSwitch.class);
            CompressStorage update = new CompressStorage(data.getVersion(), compressPushSwitch);
            if (!compareAndSet(expect, update)) {
                return false;
            }
            logger.info("Fetch session push compressed, prev={}, current={}", expect.compressPushSwitch, switchString);
            return true;
        } catch (Throwable e) {
            logger.error("Fetch session push compressed enabled error", e);
        }
        return false;
    }

    public CompressPushSwitch getCompressSwitch() {
        return storage.get().compressPushSwitch;
    }

    public Compressor getCompressor(Map<String, List<Data>> data, String[] acceptEncodes, String clientIp) {
        CompressPushSwitch compressPushSwitch = getCompressSwitch();
        if (!compressEnabled(compressPushSwitch, clientIp)) {
            return null;
        }
        if (dataBoxesMapSize(data) < compressPushSwitch.getCompressMinSize()) {
            return null;
        }
        return CompressUtils.find(acceptEncodes, compressPushSwitch.getForbidEncodes());
    }

    protected static class CompressStorage extends SystemDataStorage {
        protected final CompressPushSwitch compressPushSwitch;

        public CompressStorage(long version, CompressPushSwitch compressPushSwitch) {
            super(version);
            this.compressPushSwitch = compressPushSwitch;
        }
    }
}
